JSON Web Token 是一種用於網路上安全傳輸信息的標準。在Spring Boot應用程序中,常被用於實現身份驗證和授權機制。身份驗證過程中,當使用者成功登錄後,會生成JWT並返回給客戶端,後續將JWT包含在請求的標頭中會使用密鑰來驗證JWT的簽名,如果JWT是有效的且未過期,表示可以信任其中的聲明並授予訪問。JWT通常由三部分組成:
JWT的優點:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
</dependency>
可以使用ez2o工具生成密鑰
用於生成、解析和驗證JWT。
@Service
@Slf4j
public class JwtService {
//預設Token過期時間
private Long EXPIRATION_TIME = Long.valueOf(360000000);
//密鑰
private String SECRET_KEY ="BZDuw8k4sHCFTHVtAhUt2B7n657FHUHx2DQfVTcPpxD3yqGBwXSnD2enURrYXsQN";
/**
* 從JWT令牌中提取使用者名稱
*/
public String extractUsername(String token) {
//從JWT取得Subject聲明值
return extractClaim(token, Claims::getSubject);
}
/**
* 從JWT令牌中提取過期時間
*/
public Date extractExpiration(String token) {
//從JWT取得Expiration聲明值。
return extractClaim(token, Claims::getExpiration);
}
/**
* 從JWT令牌中提取聲明,接受claimsResolver函數,此函數用於解析JWT的聲明。
*/
public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
//用來解析JWT令牌中的所有聲明。
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}
/**
* 使用簽名密鑰解析JWT令牌中的所有聲明,並返回Claims對象
*/
private Claims extractAllClaims(String token) {
return Jwts
.parserBuilder()
.setSigningKey(createHmacSigningKeyFromSecret())
.build()
.parseClaimsJws(token)
.getBody();
}
/**
* 生成JWT令牌
*/
public String generateToken(UserDetails userDetails) {
Map<String, Object> extractClaims = new HashMap<>();
return createToken(extractClaims, userDetails.getUsername());
}
/**
* 用於設置JWT內部的聲明,並使用簽名密鑰進行簽名
*/
private String createToken(Map<String, Object> claims, String subject) {
return Jwts.builder()
.setClaims(claims)
.setSubject(subject)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(createHmacSigningKeyFromSecret(), SignatureAlgorithm.HS256)
.compact();
}
/**
* 從JWT令牌中提取過期時間與當前時間比較是否已經過期。
*/
private Boolean isTokenExpired(String token) {
final Date expirationDate = extractExpiration(token);
return expirationDate.before(new Date());
}
/**
* 從JWT令牌中提取使用者名稱,並呼叫isTokenExpired確保令牌未過期
*/
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = extractUsername(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
/**
* 獲取用於簽署JWT令牌的密鑰
*/
private Key createHmacSigningKeyFromSecret() {
// 從SECRET_KEY獲取Base64編碼的密鑰位元組數組
byte[] keyBytes = Decoders.BASE64.decode(SECRET_KEY);
// 使用Keys.hmacShaKeyFor 創建用於 HMAC 簽名的密鑰對象
return Keys.hmacShaKeyFor(keyBytes);
}
}